/*
 * F_Fat.h
 *
 *  Created on: 04/11/2011
 *      Author: utn_so
 */
#include "../../headers/GlobalHeaderPFS.h"

extern mbr bootSector;
extern uint32_t * fatRegion;
extern pthread_mutex_t mutex_fatRegion;

/*-------------------------------------------------------------------------*/
/**
Obtener la direccion del primer cluster a partir del formato en como se encuentra
 almacenado en la DirectoryEntry, juntando la parte HI y LOW
 */
uint32_t obtenerDireccionPrimerCluster(tDirectoryEntry* directoryEntry ){
	uint32_t dirPrimerCluster;

	dirPrimerCluster = ((uint32_t)(directoryEntry->dirFirstClusterHI)) << 8;
	dirPrimerCluster = dirPrimerCluster | (uint32_t) (directoryEntry->dirFirstClusterLO);

	return dirPrimerCluster;
}

/*-------------------------------------------------------------------------*/
/**
Obtener la direccion del primer cluster del directorio raiz
 */
uint32_t obtenerDireccionPrimerClusterDelDirectorioRaiz(){
	uint32_t primerCluster;
	primerCluster = ((uint32_t) bootSector.sectoresReservados + ((uint32_t)bootSector.numeroDeFat * bootSector.sectoresPorFat))/bootSector.sectoresPorCluster;
	return primerCluster;
}

/*-------------------------------------------------------------------------*/
/**
Obtener la direccion del inicio de la fatRegion
 */
uint32_t obtenerDireccionPrimerClusterDeLaFatRegion(){
	uint32_t primerCluster = bootSector.sectoresReservados/bootSector.sectoresPorCluster;
	return primerCluster;
}


/*-------------------------------------------------------------------------*/
/**
Obtener la direccion del siguiente cluster perteneciente al archivo
 */
uint32_t obtenerSiguienteCluster(uint32_t clusterN){
	return fatRegion[clusterN];
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerCantidadDeClusterLibres(){
	uint32_t var,cantCluster=0;
	uint32_t clustersTotales=bootSector.sectoresTotales/bootSector.sectoresPorCluster;
	for(var=0;var<clustersTotales-2;var++){
		if(fatRegion[var]==FAT_LIBRE)
			cantCluster++;
	}
	return cantCluster;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerCantidadDeClustersUsados(){
	uint32_t var,cantCluster=0;
		uint32_t clustersTotales=bootSector.sectoresTotales/bootSector.sectoresPorCluster;
		for(var=0;var<clustersTotales-2;var++){
			if(fatRegion[var]!=FAT_LIBRE)
				cantCluster++;
		}
		return cantCluster;
	}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerPrimerSectorDeCluster(uint32_t clusterN){
	return clusterN*bootSector.sectoresPorCluster;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerDirPrimerBloqueDeFat(){
	return bootSector.sectoresReservados*TAMANO_SECTOR/TAMANO_BLOQUE;
}


/*-------------------------------------------------------------------------*/
/**
Devuelve la direccion del bloque donde se encuentra la entrada fat
del clusterN (sirve para saber en que bloque se encuentra la FAT modificada)
 */
uint32_t obtenerDirEnDiscoDeLaEntradaFat(uint32_t clusterN){
	uint32_t entradasFATporBloque = TAMANO_BLOQUE / TAMANO_ENTRADA_FAT;
	return obtenerDirPrimerBloqueDeFat() + (uint32_t) ceil(clusterN / entradasFATporBloque);
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerClusterLibre(){
	uint32_t var;
	uint32_t cantClusters = bootSector.sectoresTotales/bootSector.sectoresPorCluster;
	for (var = 0; var<cantClusters;var++){
		if (fatRegion[var]==FAT_LIBRE)
			return var;
	}
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerUltimoClusterDelArchivo(tDirectoryEntry * archivo){

	return (obtenerClusterQueApunteA(FAT_ULTIMO,obtenerDireccionPrimerCluster(archivo)));
}

/*-------------------------------------------------------------------------*/
/**
Devuelve el cluster que apunte a cierto contenido, sirviendo para buscar el
anteúltimo cluster principalmente.
 */
uint32_t obtenerClusterQueApunteA(uint32_t clusterFin, uint32_t primerCluster){
	uint32_t anterior=primerCluster;
	while((primerCluster=obtenerSiguienteCluster(primerCluster))!=clusterFin){
		anterior=primerCluster;
	}
	return anterior;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t agregarClusterLibre(uint32_t ultimoCluster){
	pthread_mutex_lock(&mutex_fatRegion);
	uint32_t clusterLibre=obtenerClusterLibre();
	reemplazarValorFat(ultimoCluster,clusterLibre);
	reemplazarValorFat(clusterLibre,FAT_ULTIMO);
	pthread_mutex_unlock(&mutex_fatRegion);
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t agregarClusterAArchivo(tDirectoryEntry * archivo){
	if((obtenerDireccionPrimerCluster(archivo))==0){
		uint32_t nroCluster;
		pthread_mutex_lock(&mutex_fatRegion);
		nroCluster = obtenerClusterLibre();
		reemplazarValorFat(nroCluster,FAT_ULTIMO);
		agregarPrimerCluster(nroCluster,archivo);
		pthread_mutex_unlock(&mutex_fatRegion);
		return 0;
	}

	uint32_t ultimoCluster = obtenerUltimoClusterDelArchivo(archivo);
	agregarClusterLibre(ultimoCluster);
	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t removerClusterAArchivo(tDirectoryEntry * archivo){
	uint32_t ultimoCluster=obtenerUltimoClusterDelArchivo(archivo);
	uint32_t primerCluster= obtenerDireccionPrimerCluster(archivo);
	tCluster clusterVacio;
	memset(&clusterVacio,0,sizeof(tCluster));
	if(ultimoCluster==primerCluster){
		pthread_mutex_lock(&mutex_fatRegion);
		reemplazarValorFat(ultimoCluster,FAT_LIBRE);
		pthread_mutex_unlock(&mutex_fatRegion);
		pedirEscrituraDeCluster(ultimoCluster,&clusterVacio);
		agregarPrimerCluster(0,archivo);
	}else{
		pthread_mutex_lock(&mutex_fatRegion);
		uint32_t anteUltimoCluster = obtenerClusterQueApunteA(ultimoCluster,primerCluster);
		reemplazarValorFat(anteUltimoCluster,FAT_ULTIMO);
		reemplazarValorFat(ultimoCluster,FAT_LIBRE);
		pthread_mutex_unlock(&mutex_fatRegion);
		pedirEscrituraDeCluster(ultimoCluster,&clusterVacio);
	}


	return 0;
}

/*-------------------------------------------------------------------------*/
/**

 */
uint32_t obtenerCantidadDeClusters(tDirectoryEntry * archivo){
	uint32_t cantidad = 1;
	uint32_t clusterN = obtenerDireccionPrimerCluster(archivo);
	if(clusterN == 0){
		return 0;
	}
	while((clusterN = obtenerSiguienteCluster(clusterN)) != FAT_ULTIMO)
		cantidad++;
	return cantidad;
}

/*-------------------------------------------------------------------------*/
/**

 */
void agregarPrimerCluster(uint32_t nroCluster ,tDirectoryEntry * entradaDir ){
	entradaDir->dirFirstClusterLO = nroCluster & 0x0000FFFF;
	if(nroCluster>65535)
	entradaDir->dirFirstClusterHI = nroCluster >> 8;
}

/*-------------------------------------------------------------------------*/
/**
Me obtiene el cluster a leer de un archivo a partir del cluster inicio y el offset
recibido del archivo
 */
uint32_t obtenerClusterLeerXOffsetArch(uint32_t nroCluster, uint32_t offset){
	uint32_t var;
	for (var=0;var< offset;var++){
		nroCluster = obtenerSiguienteCluster(nroCluster);
		if (nroCluster==FAT_ULTIMO){
			return 0;
		}
	}
	return nroCluster;
}
